package br.com.intelsys.basics.dao.hiber;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.stat.SessionStatistics;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import br.com.intelsys.basics.dao.DaoDebugInfo;
import br.com.intelsys.basics.dao.QueryManager;

public abstract class DaoHiber extends HibernateDaoSupport {
	private static final String ATTRIBUTE_NAME = "sessSpringStatic";
	private final QueryManager queryManager;
	private static Integer sessCount = 0;
	private static List<Session> sessionList = new ArrayList<Session>();
	private Transaction transaction;
	private static final Log log = LogFactory.getLog(DaoHiber.class);
	private HttpServletRequest request;

	public DaoHiber(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
		queryManager = new QueryManagerHibernateTemplate(this);
	}

	public DaoHiber(SessionFactory sessionFactory, HttpServletRequest request) {
		setSessionFactory(sessionFactory);
		this.request = request;
		queryManager = new QueryManagerHibernateTemplate(this);
	}

	protected QueryManager getQueryManager() {
		return queryManager;
	}

	synchronized Session getLocalSession() {
		synchronized (sessCount) {
			Session sess;
			if (request != null) {
				if (log.isDebugEnabled()) {
					log.debug("Checking session from dynamic Request attribute");
				}
				sess = (Session) request.getAttribute(ATTRIBUTE_NAME);
			} else {
				if (log.isDebugEnabled()) {
					log.debug("Checking session from static Request attribute");
				}
				sess = (Session) RequestContextHolder.getRequestAttributes().getAttribute(ATTRIBUTE_NAME, RequestAttributes.SCOPE_REQUEST);
			}
			if (sess == null) {
				sess = getSession();
				sessionList.add(sess);
				RequestContextHolder.getRequestAttributes().setAttribute(ATTRIBUTE_NAME, sess, RequestAttributes.SCOPE_REQUEST);
				sessCount++;
				if (log.isDebugEnabled()) {
					log.debug("Creating a session for request from jSessionId '"
							+ RequestContextHolder.getRequestAttributes().getSessionId() + "'. Total: " + sessCount);
				}
			} else if (log.isDebugEnabled()) {
				log.debug("Using a session from '" + RequestContextHolder.getRequestAttributes().getSessionId() + "'. Total: " + sessCount);
			}
			if (log.isDebugEnabled()) {
				showLogSessionList();
			}
			return sess;
		}
	}

	public static synchronized void closeLocalSession(ServletRequest request) {
		Session sess = (Session) request.getAttribute(ATTRIBUTE_NAME);
		synchronized (sessCount) {
			if (sess != null) {
				sessCount--;
				sess.disconnect();
				sess.close();
				request.setAttribute(ATTRIBUTE_NAME, null);
				sessionList.remove(sess);
				if (log.isDebugEnabled()) {
					log.debug("Closing a session for request from sessionId '" + getUrl(request) + "'. Total: " + sessCount);
					showLogSessionList();
				}
			} else if (log.isDebugEnabled() && !((HttpServletRequest) request).getRequestURI().startsWith("/static")) {
				log.debug("No session to be closed from '" + getUrl(request) + "'. Total: " + sessCount);
				showLogSessionList();
			}
		}
	}

	private static void showLogSessionList() {
		int i = 0;
		log.debug("- sessionList - begin --------------------");
		for (Session sess : sessionList) {
			log.debug(getSessionData(sess, i++));
		}
		log.debug("- sessionList - end ----------------------");
	}

	static String getSessionData(Session sess, int count) {
		SessionStatistics ss = null;
		if (sess.isOpen()) {
			ss = sess.getStatistics();
		}
		return " - "
				+ count
				+ " open:"
				+ sess.isOpen()
				+ ", connected:"
				+ sess.isConnected()
				+ (ss != null ? ", dirt:" + sess.isDirty() + ", entityCount:" + ss.getEntityCount() + ", collectionCount:"
						+ ss.getCollectionCount() : "");
	}

	private static String getUrl(ServletRequest request) {
		HttpServletRequest req = (HttpServletRequest) request;
		try {
			return req.getSession().getId() + " :: " + req.getHeader("Host") + req.getRequestURI() + '?' + req.getQueryString();
		} catch (IllegalStateException e) {
			if (log.isErrorEnabled()) {
				log.error("Session not found", e);
			}
			return "x :: " + req.getHeader("Host") + req.getRequestURI() + '?' + req.getQueryString();
		}
	}

	public Transaction startTransaction() {
		transaction = getSession().beginTransaction();
		return transaction;
	}

	public void commit() {
		commit(transaction);
		cleanTransaction();
	}

	public void rollback() {
		rollback(transaction);
		cleanTransaction();
	}

	public void commit(Transaction transaction) {
		if (transaction != null) {
			transaction.commit();
		} else {
			log.error("ATENCAO: commit sem transaction!");
		}
	}

	public void rollback(Transaction transaction) {
		if (transaction != null) {
			transaction.rollback();
		} else {
			log.error("ATENCAO: rollback sem transaction!");
		}
	}

	public boolean isOnTransaction() {
		return transaction != null;
	}

	public Transaction startTransactionIfOff() {
		if (!isOnTransaction()) {
			startTransaction();
		}
		return transaction;
	}

	public void cleanTransaction() {
		transaction = null;
	}

	public DaoDebugInfo getDaoDebugInfo() {
		return new DaoDebugInfoHiber(sessionList, sessCount);
	}

}